package cn.iocoder.yudao.module.iot.gateway.protocol.tcp.router;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.module.iot.core.biz.IotDeviceCommonApi;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import cn.iocoder.yudao.module.iot.gateway.codec.IotDeviceMessageCodec;
import cn.iocoder.yudao.module.iot.gateway.codec.tcp.IotTcpBinaryDeviceMessageCodec;
import cn.iocoder.yudao.module.iot.gateway.codec.tcp.IotTcpJsonDeviceMessageCodec;
import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.IotTcpUpstreamProtocol;
import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.config.TcpProtocolConfig;
import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.manager.IotTcpConnectionManager;
import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.router.handler.ProtocolHandler;
import cn.iocoder.yudao.module.iot.gateway.protocol.tcp.router.handler.ProtocolHandlerRegistry;
import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService;
import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
import io.vertx.core.Handler;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.net.NetSocket;
import lombok.extern.slf4j.Slf4j;

/**
 * TCP 上行消息处理器
 *
 * @author 芋道源码
 */
@Slf4j
public class IotTcpUpstreamHandler implements Handler<NetSocket> {

    private static final String CODEC_TYPE_JSON = IotTcpJsonDeviceMessageCodec.TYPE;
    private static final String CODEC_TYPE_BINARY = IotTcpBinaryDeviceMessageCodec.TYPE;

    public static final String AUTH_METHOD = "auth";

    private final IotDeviceMessageService deviceMessageService;

    private final IotDeviceService deviceService;

    private final IotTcpConnectionManager connectionManager;

    private final IotDeviceCommonApi deviceApi;

    private final String serverId;
    private final TcpProtocolConfig config;

    private final ProtocolHandlerRegistry handlerRegistry;

    public IotTcpUpstreamHandler(IotTcpUpstreamProtocol protocol,
                                 IotDeviceMessageService deviceMessageService,
                                 IotDeviceService deviceService,
                                 IotTcpConnectionManager connectionManager, TcpProtocolConfig config) {
        this.deviceMessageService = deviceMessageService;
        this.deviceService = deviceService;
        this.connectionManager = connectionManager;
        this.config = config;
        this.deviceApi = SpringUtil.getBean(IotDeviceCommonApi.class);
        this.serverId = protocol.getServerIdMap().get(config.getPort());
        this.handlerRegistry = SpringUtil.getBean(ProtocolHandlerRegistry.class);
    }

    @Override
    public void handle(NetSocket socket) {
        String clientId = IdUtil.simpleUUID();
        log.debug("[handle][设备连接，客户端 ID: {}，地址: {}]", clientId, socket.remoteAddress());

        // 设置异常和关闭处理器
        socket.exceptionHandler(ex -> {
            log.warn("[handle][连接异常，客户端 ID: {}，地址: {}]", clientId, socket.remoteAddress());
            cleanupConnection(socket);
        });
        socket.closeHandler(v -> {
            log.debug("[handle][连接关闭，客户端 ID: {}，地址: {}]", clientId, socket.remoteAddress());
            cleanupConnection(socket);
        });

        // 设置消息处理器
        socket.handler(buffer -> {
            try {
                processMessage(clientId, buffer, socket);
            } catch (Exception e) {
                log.error("[handle][消息解码失败，断开连接，客户端 ID: {}，地址: {}，错误: {}]",
                        clientId, socket.remoteAddress(), e.getMessage());
                handleProcessException(socket, clientId, e);
            }
        });
    }

    /**
     * 处理消息
     *
     * @param clientId 客户端 ID
     * @param buffer   消息
     * @param socket   网络连接
     * @throws Exception 消息解码失败时抛出异常
     */
    private void processMessage(String clientId, Buffer buffer, NetSocket socket) throws Exception {
        // 1. 基础检查
        if (buffer == null || buffer.length() == 0) {
            return;
        }

        // 2. 获取消息格式类型
        String codecType = getMessageCodecType(buffer, socket);

        // 3. 解码消息
        IotDeviceMessage message = decodeMessage(buffer, codecType, clientId);

        // 4. 根据消息类型路由处理
        routeMessage(clientId, message, codecType, socket);
    }

    /**
     * 解码消息
     */
    private IotDeviceMessage decodeMessage(Buffer buffer, String codecType, String clientId) throws Exception {
        try {
            IotDeviceMessage message = deviceMessageService.decodeDeviceMessage(buffer.getBytes(), codecType);
            if (message == null) {
                log.warn("[decodeMessage][解码后消息为空，客户端 ID: {}]", clientId);
                return null;
            }
            return message;
        } catch (Exception e) {
            log.error("[decodeMessage][消息解码失败，客户端 ID: {}]", clientId, e);
            throw new Exception("消息解码失败: " + e.getMessage(), e);
        }
    }

    /**
     * 路由消息到相应的处理器
     */
    private void routeMessage(String clientId, IotDeviceMessage message, String codecType, NetSocket socket) {
        try {
            ProtocolHandler handler = getProtocolHandler(message);

            if (handler != null) {
                log.debug("[routeMessage][使用自定义处理器，协议: {}，处理器: {}，客户端 ID: {}]",
                        config.getProtocolType(), handler.getHandlerType(), clientId);
                handler.handle(clientId, message, codecType, socket, serverId);
            }
        } catch (Exception e) {
            log.error("[routeMessage][消息路由处理失败，客户端 ID: {}，消息方法: {}]",
                    clientId, message.getMethod(), e);
            throw e;
        }
    }

    /**
     * 获取协议处理器
     */
    private ProtocolHandler getProtocolHandler(IotDeviceMessage message) {
        if (message == null || message.getMethod() == null) {
            return null;
        }

        // 首先尝试获取特定协议的处理器
        ProtocolHandler protocolHandler;
        if (config.getAuthMethod().equals(message.getMethod())) {
            protocolHandler = handlerRegistry.getHandler(config.getProtocolType(), "auth");
        } else {
            protocolHandler = handlerRegistry.getHandler(config.getProtocolType(), "business");
        }

        // 如果找到了特定协议处理器，直接返回
        if (protocolHandler != null) {
            return protocolHandler;
        }

        // 回退到默认处理器
        ProtocolHandler defaultHandler;
        if (AUTH_METHOD.equals(message.getMethod())) {
            defaultHandler = handlerRegistry.getDefaultHandler("auth");
        } else {
            defaultHandler = handlerRegistry.getDefaultHandler("business");
        }

        if (defaultHandler != null) {
            log.debug("[getProtocolHandler][使用默认处理器，方法: {}]", message.getMethod());
        }

        return defaultHandler;
    }


    /**
     * 获取消息编解码类型
     *
     * @param buffer 消息
     * @param socket 网络连接
     * @return 消息编解码类型
     */
    private String getMessageCodecType(Buffer buffer, NetSocket socket) {
        // 1. 如果已认证，优先使用缓存的编解码类型
        IotTcpConnectionManager.ConnectionInfo connectionInfo = connectionManager.getConnectionInfo(socket);
        if (connectionInfo != null && connectionInfo.isAuthenticated() &&
                StrUtil.isNotBlank(connectionInfo.getCodecType())) {
            return connectionInfo.getCodecType();
        }

        // 2. 根据 TcpProtocolConfig 指定编解码器类型
        IotDeviceMessageCodec customCodec = getCodecInstance();
        if (customCodec != null) {
            return customCodec.type();
        }

        // 3. 未认证时检测消息格式类型
        return IotTcpBinaryDeviceMessageCodec.isBinaryFormatQuick(buffer.getBytes()) ? CODEC_TYPE_BINARY
                : CODEC_TYPE_JSON;
    }

    /**
     * 根据配置获取编解码器实例
     *
     * @return 编解码器实例
     */
    private IotDeviceMessageCodec getCodecInstance() {
        if (config.getCodecClass() != null) {
            try {
                return config.getCodecClass().getDeclaredConstructor().newInstance();
            } catch (Exception e) {
                log.error("[getCodecInstance][创建编解码器实例失败]", e);
            }
        }
        return null;
    }


    /**
     * 清理连接
     *
     * @param socket 网络连接
     */
    private void cleanupConnection(NetSocket socket) {
        try {
            // 1. 发送离线消息（如果已认证）
            IotTcpConnectionManager.ConnectionInfo connectionInfo = connectionManager.getConnectionInfo(socket);
            if (connectionInfo != null) {
                IotDeviceMessage offlineMessage = IotDeviceMessage.buildStateOffline();
                deviceMessageService.sendDeviceMessage(offlineMessage, connectionInfo.getProductKey(),
                        connectionInfo.getDeviceName(), serverId);
            }

            // 2. 注销连接
            connectionManager.unregisterConnection(socket);
        } catch (Exception e) {
            log.error("[cleanupConnection][清理连接失败]", e);
        }
    }


    /**
     * 处理消息处理异常
     */
    private void handleProcessException(NetSocket socket, String clientId, Exception e) {
        try {
            // 发送错误响应给客户端（如果可能）
            // 这里可以根据具体需求实现
            // 清理连接
            cleanupConnection(socket);
            // 关闭连接
            socket.close();
        } catch (Exception closeException) {
            log.error("[handleProcessException][关闭连接失败，客户端 ID: {}]", clientId, closeException);
        }
    }
}